home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
x11
/
rpg
/
crossfir.92
/
crossfir
/
crossfire-0.92.5
/
server
/
spell_effect.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-24
|
86KB
|
2,811 lines
/*
* static char *rcsid_newspells_c =
* "$Id: spell_effect.c,v 1.12 1996/07/24 07:42:42 master Exp master $";
*/
/*
CrossFire, A Multiplayer game for X-windows
Copyright (C) 1992 Frank Tore Johansen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The author can be reached via e-mail to frankj@ifi.uio.no.
*/
#include <global.h>
#include <object.h>
#include <living.h>
#ifndef __CEXTRACT__
#include <sproto.h>
#endif
#include <spells.h>
#ifdef SOUND_EFFECTS
#include <sounds.h>
#endif
extern object *objects;
/*
* spell_failure() handles the various effects for differing degrees
* of failure badness.
*/
#ifdef SPELL_FAILURE_EFFECTS
void spell_failure(object *op, int failure,int power)
{
if(failure<= -20&&failure > -40) /* wonder */
{
new_draw_info(NDI_UNIQUE, 0,op,"Your spell causes an unexpected effect.");
cast_cone(op,0,10,SP_WOW,spellarch[SP_WOW],0);
}
else if (failure <= -40&&failure > -60) /* confusion */
{
new_draw_info(NDI_UNIQUE, 0,op,"Your magic recoils on you!");
confuse_player(op,op,99);
}
else if (failure <= -60&&failure> -80) /* paralysis */
{
new_draw_info(NDI_UNIQUE, 0,op,"Your magic recoils on you!");
paralyze_player(op,op,99);
}
else if (failure <= -80) /* blast the immediate area */
{ object *tmp;
new_draw_info(NDI_UNIQUE, 0,op,"You lose control of the mana! The uncontrolled magic blasts you!");
tmp=get_archetype("loose_magic");
/*
tmp->level=op->level;
*/
tmp->level=SK_level(op);
tmp->x=op->x;tmp->y=op->y;
tmp->stats.hp+=power/5; /* increase the area of destruction a little for more powerful spells */
tmp->stats.dam=power; /* nasty recoils! */
tmp->stats.maxhp=tmp->count; /*??*/
insert_ob_in_map(tmp,op->map);
}
}
#endif
/* Oct 95 - hacked on this to bring in cosmetic differences for MULTIPLE_GOD hack -b.t. */
void prayer_failure(object *op, int failure,int power)
{
#ifdef MULTIPLE_GODS
char *godname;
if(!strcmp((godname=determine_god(op)),"none")) godname="Your spirit";
#endif
if(failure<= -20&&failure > -40) /* wonder */
{
#ifdef MULTIPLE_GODS
new_draw_info_format(NDI_UNIQUE, 0,op,"%s gives a sign to renew your faith.",godname);
#else
new_draw_info(NDI_UNIQUE, 0,op,"God gives a sign to renew your faith.");
#endif
cast_cone(op,0,10,SP_WOW,spellarch[SP_WOW],0);
}
else if (failure <= -40&&failure > -60) /* confusion */
{
new_draw_info(NDI_UNIQUE, 0,op,"Your diety touches your mind!");
confuse_player(op,op,99);
}
else if (failure <= -60&&failure> -80) /* paralysis */
{
#ifdef MULTIPLE_GODS
new_draw_info_format(NDI_UNIQUE, 0,op,"%s requires you to pray NOW.",godname);
#else
new_draw_info(NDI_UNIQUE, 0,op,"Your god requires you to pray NOW.");
#endif
new_draw_info(NDI_UNIQUE, 0,op,"You comply, ignoring all else.");
paralyze_player(op,op,99);
}
else if (failure <= -80) /* blast the immediate area */
{
#ifdef MULTIPLE_GODS
new_draw_info_format(NDI_UNIQUE, 0,op,"%s smites you!",godname);
#else
new_draw_info(NDI_UNIQUE, 0,op,"God smites you!");
#endif
cast_mana_storm(op,power);
}
}
void cast_mana_storm(object *op, int lvl) {
object *tmp=get_archetype("loose_magic");
tmp->level=SK_level(op);
tmp->x=op->x;tmp->y=op->y;
tmp->stats.hp+=lvl/5; /* increase the area of destruction */
tmp->stats.dam=lvl; /* nasty recoils! */
tmp->stats.maxhp=tmp->count; /*??*/
insert_ob_in_map(tmp,op->map);
}
void aggravate_monsters(object *op) {
int i,j;
object *tmp;
spell_effect(SP_AGGRAVATION, op->x, op->y, op->map);
for (i = 0; i < op->map->mapx; i++)
for (j = 0; j < op->map->mapy; j++) {
if (out_of_map(op->map, op->x + i , op->y + j))
continue;
for (tmp = get_map_ob(op->map, op->x + i, op->y + j);
tmp; tmp = tmp->above)
if (QUERY_FLAG(tmp, FLAG_MONSTER)) {
CLEAR_FLAG(tmp, FLAG_SLEEP);
if (!QUERY_FLAG(tmp, FLAG_FRIENDLY))
tmp->enemy = op;
}
}
}
int recharge(object *op) {
object *wand;
for(wand = op->inv; wand != NULL; wand = wand->below)
if(wand->type == WAND && QUERY_FLAG(wand, FLAG_APPLIED))
break;
if(wand == NULL)
return 0;
if(!(RANDOM()%4)) {
new_draw_info_format(NDI_UNIQUE, 0, op,
"The %s vibrates violently, then explodes!",query_name(wand));
#ifdef SOUND_EFFECTS
play_sound_map(op->map, op->x, op->y, SOUND_OB_EXPLODE);
#endif
spell_effect(SP_DESTRUCTION, op->x, op->y, op->map);
remove_ob(wand);
free_object(wand);
return 1;
}
new_draw_info_format(NDI_UNIQUE, 0, op,
"The %s glows with power.",query_name(wand));
wand->stats.food += RANDOM()%spells[wand->stats.sp].charges + 1;
if(wand->arch&&QUERY_FLAG(&wand->arch->clone, FLAG_ANIMATE))
{
SET_FLAG(wand, FLAG_ANIMATE);
wand->speed = wand->arch->clone.speed;
update_ob_speed(wand);
}
return 1;
}
#define MAX_ALT 80
void polymorph_living(object *op) {
archetype *altern[MAX_ALT], *at;
int nr = 0, x = op->x, y = op->y, choice, friendly;
mapstruct *map = op->map;
object *tmp, *next, *owner;
if(op->head != NULL || op->more != NULL)
return;
for(at = first_archetype ; at != NULL; at = at->next)
if(QUERY_FLAG((&at->clone),FLAG_MONSTER) == QUERY_FLAG(op, FLAG_MONSTER) &&
QUERY_FLAG((&at->clone),FLAG_GENERATOR) == QUERY_FLAG(op,FLAG_GENERATOR) &&
at->more == NULL && EDITABLE((&at->clone)))
{
altern[nr] = at;
if(++nr == MAX_ALT)
break;
}
if(!nr)
return;
choice = RANDOM()%nr;
for(tmp = op->inv; tmp != NULL; tmp = next) {
next = tmp->below;
if(QUERY_FLAG(tmp, FLAG_APPLIED))
apply(op,tmp);
if(tmp->type == ABILITY) {
remove_ob(tmp);
free_object(tmp);
}
}
remove_ob(op);
owner = get_owner(op);
friendly = QUERY_FLAG(op, FLAG_FRIENDLY);
if (friendly)
remove_friendly_object(op);
copy_object(&(altern[choice]->clone),op);
if (owner != NULL)
set_owner(op,owner);
if (friendly) {
SET_FLAG(op, FLAG_FRIENDLY);
op->move_type = PETMOVE;
add_friendly_object(op);
}
op->x = x; op->y = y;
insert_ob_in_map(op,map);
update_object(op);
if(op->arch->randomitems != NULL)
create_treasure(op->arch->randomitems,op,GT_INVISIBLE,map->difficulty,0);
for(tmp = op->inv, nr = 0; tmp != NULL && ++nr < 20; tmp = next) {
next = tmp->below;
(void) monster_check_apply(op,tmp);
}
}
/* Destroys item from polymorph failure */
void polymorph_melt(object *who, object *op)
{
new_draw_info_format(NDI_UNIQUE, 0, who,
"%s%s glows red, melts and evaporates!",
op->nrof?"":"The ",query_name(op));
#ifdef SOUND_EFFECTS
play_sound_map(op->map, op->x, op->y, SOUND_OB_EVAPORATE);
#endif
remove_ob(op);
free_object(op);
return;
}
void polymorph_item(object *who, object *op) {
archetype *altern[MAX_ALT], *at;
int nr = 0, max_value, difficulty, tries=0,choice, charges=op->stats.food;
object *new_ob;
max_value = op->value * 2;
if(max_value > 20000)
max_value = 20000 + (max_value - 20000) / 2;
for(at = first_archetype ; at != NULL; at = at->next)
if(at->clone.type == op->type)
{
altern[nr] = at;
if(++nr == MAX_ALT)
break;
}
if(!nr)
return;
difficulty = op->magic * 5;
if (difficulty<0) difficulty=0;
new_ob = get_object();
do {
choice = RANDOM()%nr;
copy_object(&(altern[choice]->clone),new_ob);
fix_generated_item(new_ob,op,difficulty,FABS(op->magic));
++tries;
} while (new_ob->value > max_value && tries<10);
if (tries==10) {
polymorph_melt(who, op);
free_object(new_ob);
return;
}
if(op->nrof && new_ob->nrof) {
new_ob->nrof = op->nrof;
/* decrease the number of items */
if (new_ob->nrof>2) new_ob->nrof -= RANDOM() % (op->nrof/2);
}
/* We don't want rings to keep sustenance/hungry status. There are propably
other cases too that should be checked. */
if(charges && op->type != RING && op->type != FOOD)
op->stats.food = charges;
new_ob->x = op->x;
new_ob->y = op->y;
remove_ob(op);
free_object(op);
insert_ob_in_map_simple(new_ob,who->map);
}
void polymorph(object *op, object *who) {
int tmp;
if(op->type == PLAYER)
return; /* WILL add this later 8) */
if(QUERY_FLAG(op, FLAG_MONSTER) || QUERY_FLAG(op,FLAG_GENERATOR)) {
polymorph_living(op);
return;
}
if(QUERY_FLAG(op, FLAG_ALIVE))
return;
if(FABS(op->speed) > 0.001 && !QUERY_FLAG(op, FLAG_ANIMATE))
return; /* Don't want to morph flying arrows, etc... */
if(op->type == 0 || op->type==PLAYER || op->arch == NULL ||
QUERY_FLAG(op,FLAG_NO_PICK)
|| QUERY_FLAG(op, FLAG_NO_PASS) || op->type == TREASURE)
return;
tmp = RANDOM() % 8;
if (tmp) polymorph_item(who, op);
else polymorph_melt(who, op);
}
/* allows the choice of what sort of food object to make.
If stringarg is NULL, it will create food dependent on level --PeterM*/
int cast_create_food(object *op,int dir, char *stringarg)
{
int food_value;
archetype *at=NULL;
object *new_op;
food_value=SP_PARAMETERS[SP_CREATE_FOOD].bdam
+ 50 * SP_level_dam_adjust(op,SP_CREATE_FOOD);
if(stringarg) {
at=find_archetype(stringarg);
if (at==NULL || ((at->clone.type != FOOD && at->clone.type != DRINK)
|| (at->clone.stats.food > food_value)))
stringarg = NULL;
}
if(!stringarg) {
if(food_value > 499)
stringarg="waybread";
else if(food_value > 399)
stringarg="dragon_steak";
else if(food_value > 199)
stringarg="food";
else if(food_value > 124)
stringarg="cake";
else stringarg="booze";
at = find_archetype(stringarg);
}
food_value/=at->clone.stats.food;
new_op = get_object();
copy_object(&at->clone, new_op);
new_op->nrof = food_value;
if (new_op->nrof<1) new_op->nrof = 1;
cast_create_obj(op, new_op, dir);
return 1;
}
int cast_polymorph(object *op, int dir) {
object *tmp, *next;
int range;
archetype *poly;
if(dir == 0)
return 0;
poly = find_archetype("polymorph");
for(range = 1;;range++) {
int x=op->x+freearr_x[dir]*range,y=op->y+freearr_y[dir]*range;
object *image;
if(wall(op->map,x,y) || blocks_magic(op->map,x,y))
break;
for(tmp = get_map_ob(op->map,x,y); tmp != NULL && tmp->above != NULL;
tmp = tmp->above);
while (tmp!=NULL) {
next = tmp->below;
polymorph(tmp, op);
tmp = next;
}
image = arch_to_object(poly);
image->x = x; image->y = y;
image->stats.food += range;
image->speed_left = 0.1;
insert_ob_in_map(image,op->map);
}
return 1;
}
int cast_speedball(object *op, int dir, int type) {
object *spb;
if(blocked(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir]))
return 0;
spb=clone_arch(SPEEDBALL);
spb->x=op->x+freearr_x[dir],spb->y=op->y+freearr_y[dir];
spb->speed_left= -0.1;
if(type==SP_LARGE_SPEEDBALL)
spb->stats.dam=30;
insert_ob_in_map(spb,op->map);
return 1;
}
int probe(object *op, int dir) {
int r;
object *tmp;
if(!dir) {
examine(op,op);
return 1;
}
for(r=1;;r++) {
int x=op->x+r*freearr_x[dir],y=op->y+r*freearr_y[dir];
if(out_of_map(op->map,x,y))
break;
if(blocks_magic(op->map,x,y)) {
new_draw_info(NDI_UNIQUE, 0,op,"Something blocks your magic.");
return 0;
}
for(tmp=get_map_ob(op->map,x,y);tmp!=NULL;tmp=tmp->above)
if(QUERY_FLAG(tmp, FLAG_ALIVE)&&(tmp->type==PLAYER||QUERY_FLAG(tmp, FLAG_MONSTER))) {
new_draw_info(NDI_UNIQUE, 0,op,"You detect something.");
if(tmp->head!=NULL)
tmp=tmp->head;
examine_monster(op,tmp);
return 1;
}
}
new_draw_info(NDI_UNIQUE, 0,op,"You detect nothing.");
return 1;
}
int cast_invisible(object *op, int spell_type) {
object *tmp;
if(op->invisible>1000) {
new_draw_info(NDI_UNIQUE, 0,op,"You are already as invisible as you can get.");
return 0;
}
switch(spell_type) {
case SP_INVIS:
CLEAR_FLAG(op, FLAG_UNDEAD);
op->invisible+=SP_PARAMETERS[spell_type].bdur; /* set the base */
op->invisible+=SP_PARAMETERS[spell_type].ldam *
SP_level_strength_adjust(op,spell_type); /* set the level bonus */
if(op->type==PLAYER)
op->contr->tmp_invis=1;
break;
case SP_INVIS_UNDEAD:
SET_FLAG(op, FLAG_UNDEAD);
op->invisible+=SP_PARAMETERS[spell_type].bdur; /* set the base */
op->invisible+=SP_PARAMETERS[spell_type].ldam *
SP_level_strength_adjust(op,spell_type); /* set the level bonus */
if(op->type==PLAYER)
op->contr->tmp_invis=1;
break;
case SP_IMPROVED_INVIS:
op->invisible+=SP_PARAMETERS[spell_type].bdur; /* set the base */
op->invisible+=SP_PARAMETERS[spell_type].ldam *
SP_level_strength_adjust(op,spell_type); /* set the level bonus */
break;
}
new_draw_info(NDI_UNIQUE, 0,op,"You can't see your hands!");
update_object(op);
for (tmp = objects; tmp != NULL; tmp = tmp->next)
if (tmp->enemy == op)
tmp->enemy = NULL;
return 1;
}
int
cast_earth2dust( object *op ) {
object *tmp, *next;
int strength,i,j;
if(op->type!=PLAYER)
return 0;
strength=SP_PARAMETERS[SP_EARTH_DUST].bdur + SP_level_strength_adjust(op,SP_EARTH_DUST);
strength=(strength>15)?15:strength;
for(i= -strength;i<strength;i++)
for(j= -strength;j<strength;j++) {
if(out_of_map(op->map,op->x+i,op->y+j))
continue;
for(tmp=get_map_ob(op->map,op->x+i,op->y+j);tmp!=NULL;tmp=next) {
next=tmp->above;
if(tmp&&QUERY_FLAG(tmp, FLAG_TEAR_DOWN))
hit_player(tmp,9999,op,AT_PHYSICAL);
}
}
return 1;
}
/* puts a 'WORD_OF_RECALL_' object in player */
int cast_wor(object *op) {
object *dummy;
if(op->type!=PLAYER)
return 0;
if(blocks_magic(op->map,op->x,op->y)) {
new_draw_info(NDI_UNIQUE, 0,op,"Something blocks your spell.");
return 0;
}
dummy=get_object();
if(dummy == NULL){
new_draw_info(NDI_UNIQUE, 0,op,"Oops, program error!");
LOG(llevError,"get_object failed!\n");
return 0;
}
if(op->owner) op=op->owner; /* better insert the spell in the player */
dummy->invisible=1;
dummy->speed = 0.01;
update_ob_speed(dummy);
dummy->speed_left= -1;
dummy->type=WORD_OF_RECALL;
EXIT_PATH(dummy)=add_string(first_map_path);
(void) insert_ob_in_ob(dummy,op);
new_draw_info(NDI_UNIQUE, 0,op,"You feel a force starting to build up inside you.");
return 1;
}
int cast_wow(object *op, int dir, int ability, SpellTypeFrom item) {
int sp;
if(!(RANDOM()%4))
return cast_cone(op,0,10,SP_WOW,spellarch[SP_WOW],0);
do
sp=RANDOM()%NROFREALSPELLS;
while (!spells[sp].books);
return cast_spell(op,op,dir,sp,ability,item,NULL);
}
int perceive_self(object *op) {
char *cp=describe_item(op), buf[MAX_BUF];
archetype *at=find_archetype("depletion");
object *tmp;
int i;
tmp=present_arch_in_ob(at,op);
if(*cp=='\0' && tmp==NULL)
new_draw_info(NDI_UNIQUE, 0,op,"You feel very mundane");
else {
new_draw_info(NDI_UNIQUE, 0,op,"You have:");
new_draw_info(NDI_UNIQUE, 0,op,cp);
if (tmp!=NULL) {
for (i=0; i<7; i++) {
if (get_attr_value(&tmp->stats, i)<0) {
sprintf(buf,"Your %s is depleted by %d", statname[i],
-(get_attr_value(&tmp->stats,i)));
new_draw_info(NDI_UNIQUE, 0,op, buf);
}
}
}
}
return 1;
}
int cast_destruction(object *op, int dam, int attacktype) {
int i,j;
int r; /* peterm: added to make area of effect level dep. */
object *tmp;
if(op->type!=PLAYER)
return 0;
r=5 + SP_level_strength_adjust(op,SP_DESTRUCTION);
dam+=SP_level_dam_adjust(op,SP_DESTRUCTION);
for(i= -r;i<r;i++)
for(j= -r;j<r;j++) {
if(out_of_map(op->map,op->x+i,op->y+j))
continue;
tmp=get_map_ob(op->map,op->x+i,op->y+j);
while(tmp!=NULL&&(!QUERY_FLAG(tmp, FLAG_ALIVE)||tmp->type==PLAYER))
tmp=tmp->above;
if(tmp==NULL)
continue;
hit_player(tmp,dam,op,attacktype);
}
return 1;
}
int magic_wall(object *op,int dir,int spell_type) {
object *tmp;
if(!dir) {
new_draw_info(NDI_UNIQUE, 0,op,"In what direction?");
return 0;
}
if(blocked(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir])) {
new_draw_info(NDI_UNIQUE, 0,op,"Something is in the way.");
return 0;
}
switch(spell_type) {
case SP_EARTH_WALL:
tmp=get_archetype("earthwall");
tmp->immune=0,tmp->protected=0;
tmp->stats.hp = SP_PARAMETERS[spell_type].bdur +
10* SP_level_strength_adjust(op,spell_type);
/* More solid, since they can be torn down */
tmp->stats.maxhp = tmp->stats.hp;
#if 0
/* Don't make them friendly - ends up making them too hard hard for other
* players to kill
*/
/*
* Now, make agressive monsters tear down the walls:
*/
SET_FLAG(tmp, FLAG_FRIENDLY);
add_friendly_object(tmp);
#endif
break;
case SP_FIRE_WALL:
tmp=get_archetype("firebreath");
tmp->attacktype |= AT_MAGIC;
tmp->stats.hp=SP_PARAMETERS[spell_type].bdur
+ 5*SP_level_strength_adjust(op,spell_type);
tmp->stats.dam=SP_PARAMETERS[spell_type].bdam
+SP_level_dam_adjust(op,spell_type);
tmp->stats.food=1; /* so it doesn't propagate */
#if 0
SET_SLOW_MOVE(tmp);
SET_SLOW_PENALTY(tmp,2);
#endif
SET_FLAG(tmp, FLAG_WALK_ON);
SET_FLAG(tmp, FLAG_FLY_ON);
set_owner(tmp,op);
break;
case SP_FROST_WALL:
tmp=get_archetype("icestorm");
tmp->attacktype |= AT_MAGIC;
tmp->stats.hp=SP_PARAMETERS[spell_type].bdur
+ 5*SP_level_strength_adjust(op,spell_type);
tmp->stats.dam=SP_PARAMETERS[spell_type].bdam
+SP_level_dam_adjust(op,spell_type);
tmp->stats.food=1; /* so it doesn't propagate */
#if 0
SET_SLOW_MOVE(tmp);
SET_SLOW_PENALTY(tmp,3);
#endif
SET_FLAG(tmp, FLAG_WALK_ON);
SET_FLAG(tmp, FLAG_FLY_ON);
set_owner(tmp,op);
break;
case SP_WALL_OF_THORNS:
tmp=get_archetype("thorns");
tmp->stats.hp=SP_PARAMETERS[spell_type].bdur
+ 5*SP_level_strength_adjust(op,spell_type);
tmp->stats.dam=SP_PARAMETERS[spell_type].bdam
+SP_level_dam_adjust(op,spell_type);
SET_FLAG(tmp, FLAG_WALK_ON);
SET_FLAG(tmp, FLAG_FLY_ON);
SET_FLAG(tmp, FLAG_BLOCKSVIEW);
set_owner(tmp,op);
break;
case SP_CHAOS_POOL:
tmp=get_archetype("color_spray");
tmp->attacktype|=AT_MAGIC;
tmp->stats.hp=SP_PARAMETERS[spell_type].bdur
+ 5*SP_level_strength_adjust(op,spell_type);
tmp->stats.dam=SP_PARAMETERS[spell_type].bdam
+SP_level_dam_adjust(op,spell_type);
tmp->stats.food=1; /* so the color spray object won't propagate */
SET_FLAG(tmp, FLAG_WALK_ON);
SET_FLAG(tmp, FLAG_FLY_ON);
set_owner(tmp,op);
break;
case SP_DARKNESS:
tmp=get_archetype("darkness");
tmp->speed = 0.000001 * (SP_PARAMETERS[SP_DARKNESS].bdur
- (10*SP_level_strength_adjust(op,SP_DARKNESS)));
break;
case SP_COUNTERWALL:
tmp=get_archetype("counterspell");
tmp->attacktype|=AT_MAGIC;
tmp->stats.hp=SP_PARAMETERS[spell_type].bdur
+ 5*SP_level_strength_adjust(op,spell_type);
tmp->stats.dam=SP_PARAMETERS[spell_type].bdam
+SP_level_dam_adjust(op,spell_type);
tmp->stats.food=1;
/*
tmp->level=op->level;
*/
tmp->level=SK_level(op);
SET_FLAG(tmp, FLAG_WALK_ON);
SET_FLAG(tmp, FLAG_FLY_ON);
set_owner(tmp,op);
break;
default:
LOG(llevError,"Unimplemented magic_wall spell: %d\n",spell_type);
return 0;
}
tmp->x=op->x+freearr_x[dir],tmp->y=op->y+freearr_y[dir];
insert_ob_in_map(tmp,op->map);
if(QUERY_FLAG(tmp, FLAG_BLOCKSVIEW))
update_all_los(op->map);
if(op->type==PLAYER)
draw(op);
else
SET_FLAG(op, FLAG_SCARED); /* We don't want them to walk through the wall! */
return 1;
}
/* cast_light() - I wanted this to be able to blind opponents who stand
* adjacent to the caster, so I couldnt use magic_wall(). -b.t.
*/
int cast_light(object *op,int dir) {
object *target=NULL,*tmp=NULL;
int x,y,dam=SP_PARAMETERS[SP_LIGHT].bdam
+SP_level_dam_adjust(op,SP_LIGHT);;
if(!dir) {
new_draw_info(NDI_UNIQUE, 0,op,"In what direction?");
return 0;
}
x=op->x+freearr_x[dir],y=op->y+freearr_y[dir];
for(target=get_map_ob(op->map,x,y);target;target=target->above)
if(QUERY_FLAG(target,FLAG_MONSTER)) {
/* coky doky. got a target monster. Lets make a blinding attack */
if(target->head) target = target->head;
(void) hit_player(target,dam,op,(AT_BLIND|AT_MAGIC));
return 1; /* one success only! */
}
/* no live target, perhaps a wall is in the way? */
if(blocked(op->map,x,y)) {
new_draw_info(NDI_UNIQUE, 0,op,"Something is in the way.");
return 0;
}
/* ok, looks groovy to just insert a new light on the map */
tmp=get_archetype("light");
if(!tmp) {
LOG(llevError,"Error: spell arch for cast_light() missing.\n");
return 0;
}
tmp->speed = 0.000001 * (SP_PARAMETERS[SP_LIGHT].bdur
- (10*SP_level_strength_adjust(op,SP_LIGHT)));
tmp->glow_radius=dam;
tmp->x=x,tmp->y=y;
if(tmp->speed<=0) tmp->speed = 0.000001; /* safety */
insert_ob_in_map(tmp,op->map);
if(op->type==PLAYER) draw(op);
return 1;
}
int dimension_door(object *op,int dir) {
int dist;
if(!dir) {
new_draw_info(NDI_UNIQUE, 0,op,"In what direction?");
return 0;
}
if(op->type!=PLAYER)
return 0;
if(op->contr->count) {
for(dist=0;dist<op->contr->count&&
!blocks_magic(op->map,op->x+freearr_x[dir]*(dist+1),
op->y+freearr_y[dir]*(dist+1));
dist++);
if(dist<op->contr->count) {
new_draw_info(NDI_UNIQUE, 0,op,"Something blocks your magic.\n");
op->contr->count=0;
return 0;
}
op->contr->count=0;
if(blocked(op->map,op->x+freearr_x[dir]*dist,
op->y+freearr_y[dir]*dist))
{ /* Now the fun starts... 8) */
int x=RANDOM()%op->map->mapx,y=RANDOM()%op->map->mapy;
if(blocked(op->map,x,y)) { /* Lucky after all... */
new_draw_info(NDI_UNIQUE, 0,op,"You cast your spell, but nothing happens.\n");
return 1; /* Maybe the penalty should be more severe... */
}
remove_ob(op);
op->x=x,op->y=y;
insert_ob_in_map(op,op->map);
draw(op);
return 1;
}
} /* if op->contr->count*/ else {
for(dist=0;!blocks_view (op->map,op->x+freearr_x[dir]*(dist+1),
op->y+freearr_y[dir]*(dist+1))&&
!blocks_magic(op->map,op->x+freearr_x[dir]*(dist+1),
op->y+freearr_y[dir]*(dist+1));
dist++);
for(;dist>0&&blocked(op->map,op->x+freearr_x[dir]*dist,
op->y+freearr_y[dir]*dist);dist--);
if(!dist) {
new_draw_info(NDI_UNIQUE, 0,op,"Your spell failed!\n");
return 0;
}
}
remove_ob(op);
op->x+=freearr_x[dir]*dist,op->y+=freearr_y[dir]*dist;
insert_ob_in_map(op,op->map);
draw(op);
op->speed_left= -FABS(op->speed)*5; /* Freeze them for a short while */
return 1;
}
int
cast_heal(object *op,int dir,int spell_type) {
object *tmp;
archetype *at;
object *poison;
int heal=0;
tmp = find_target_for_friendly_spell(op,dir);
if(tmp==NULL) return 0;
switch(spell_type) {
case SP_MINOR_HEAL:
heal=(RANDOM()%7)+1;
new_draw_info(NDI_UNIQUE, 0,tmp, "Your wounds start to close.");
break;
case SP_MED_HEAL:
heal=(RANDOM()%6)+(RANDOM()%6)+(RANDOM()%6)+7;
new_draw_info(NDI_UNIQUE, 0,tmp, "Your wounds start to fade.");
break;
case SP_MAJOR_HEAL:
new_draw_info(NDI_UNIQUE, 0,tmp, "Your skin looks as good as new!");
heal=(RANDOM()%8)+(RANDOM()%8)+(RANDOM()%8)+(RANDOM()%8)+12;
break;
case SP_HEAL:
heal=tmp->stats.maxhp; /* or should be this tmp->stats.maxhp? */
new_draw_info(NDI_UNIQUE, 0,tmp, "You feel just fine!");
break;
case SP_CURE_POISON:
at = find_archetype("poisoning");
poison=present_arch_in_ob(at,tmp);
if (poison) {
new_draw_info(NDI_UNIQUE, 0,tmp, "Your body feels cleansed");
poison->stats.food = 1;
}
break;
case SP_CURE_CONFUSION:
at=find_archetype("confusion");
poison=present_arch_in_ob(at,tmp);
if (poison) {
new_draw_info(NDI_UNIQUE, 0,tmp, "Your mind feels clearer");
poison->stats.food = 1;
}
break;
case SP_CURE_BLINDNESS:
at=find_archetype("blindness");
poison=present_arch_in_ob(at,tmp);
if (poison) {
new_draw_info(NDI_UNIQUE, 0,tmp,"Your vision begins to return.");
poison->stats.food = 1;
}
break;
case SP_RESTORATION: /* does cure poison, cure madness, heal,and removes depletion, food=999. */
cast_heal(op,dir,SP_CURE_POISON);
cast_heal(op,dir,SP_CURE_CONFUSION);
tmp->stats.food=999;
#if 0
/* Leave removing depletion to the restore potion. */
at=find_archetype("depletion");
poison=present_arch_in_ob(at,tmp);
if (poison) {
new_draw_info(NDI_UNIQUE, 0,tmp, "Your abilities seem to have recovered.");
remove_ob(poison);
free_object(poison);
poison = present_arch_in_ob(at, tmp);
}
#endif
cast_heal(op,dir,SP_HEAL); /* put this one last because
it'll redraw the stats
as a side effect. */
return 1;
}
if (tmp->stats.hp==tmp->stats.maxhp)
return 0;
tmp->stats.hp+=heal;
if(tmp->stats.hp>tmp->stats.maxhp)
tmp->stats.hp=tmp->stats.maxhp;
if(tmp->type==PLAYER)
draw_stats(tmp);
if(op!=tmp && op->type==PLAYER)
draw_stats(op);
op->speed_left= -FABS(op->speed)*3; /* Freeze them for a short while */
return 1;
}
int cast_regenerate_spellpoints(object *op) {
object *tmp;
tmp = find_target_for_friendly_spell(op,0);
if(tmp==NULL) return 0;
tmp->stats.sp = tmp->stats.maxsp;
if (tmp->type == PLAYER)
draw_stats(tmp);
new_draw_info(NDI_UNIQUE, 0,tmp, "Magical energies surge through your body!");
return 1;
}
int
cast_change_attr(object *op,int dir,int spell_type) {
object *tmp = op;
object *force;
int i;
/* if dir = 99 op defaults to tmp, eat_special_food() requires this. */
if(dir!=99)
tmp=find_target_for_friendly_spell(op,dir);
if(tmp==NULL) return 0;
/* if((force=present_in_ob(FORCE,tmp))!=NULL)
remove_force(force);
We should be able to have more than one thing in force! */
force=get_archetype("force");
switch(spell_type) {
case SP_STRENGTH:
if(tmp->type!=PLAYER)
break;
if(!(RANDOM()%(MAX(1,(10 - MAX_STAT + tmp->stats.Str))))) {
for(i=20,force->stats.Str=1;i>tmp->stats.Str;i-=2)
force->stats.Str++; }
else { new_draw_info(NDI_UNIQUE, 0,op,"You grow no stronger."); force->stats.Str=0; }
break;
case SP_DEXTERITY:
if(tmp->type!=PLAYER)
break;
if(!(RANDOM()%(MAX(1,(10 - MAX_STAT + tmp->stats.Dex))))) {
for(i=20,force->stats.Dex=1;i>tmp->stats.Dex;i-=2)
force->stats.Dex++; }
else { new_draw_info(NDI_UNIQUE, 0,op,"You grow no more agile."); force->stats.Dex=0; }
break;
case SP_CONSTITUTION:
if(tmp->type!=PLAYER)
break;
if(!(RANDOM()%(MAX(1,(10 - MAX_STAT + tmp->stats.Con))))) {
for(i=20,force->stats.Con=1;i>tmp->stats.Con;i-=2)
force->stats.Con++;}
else { new_draw_info(NDI_UNIQUE, 0,op,"You don't feel any healthier."); force->stats.Con=0; }
break;
case SP_CHARISMA:
if(tmp->type!=PLAYER)
break;
if(!(RANDOM()%(MAX(1,(10 - MAX_STAT + tmp->stats.Cha))))) {
for(i=20,force->stats.Cha=1;i>tmp->stats.Cha;i-=2)
force->stats.Cha++;}
else { new_draw_info(NDI_UNIQUE, 0,op,"You are no easier to look at."); force->stats.Cha=0; }
break;
case SP_ARMOUR: {
/* peterm, modified so that it uses my functions */
force->stats.ac=2+SP_level_dam_adjust(op,spell_type);
if((tmp->stats.ac-force->stats.ac)<-20)
force->stats.ac=tmp->stats.ac+20;
/* if(force->stats.ac>5)
force->stats.ac=5; */
force->armour = 5+4*SP_level_dam_adjust(op,spell_type);
if (force->armour > 25)
force->armour = 25;
if(tmp->armour>70&& force->armour>(100-tmp->armour)/3)
force->armour=3; /* diminishing returns at high armor. */
new_draw_info(NDI_UNIQUE, 0,tmp,"A force shimmers around you.");
break; }
case SP_CONFUSION:
force->attacktype |= (AT_CONFUSION|AT_PHYSICAL);
force->protected |= AT_CONFUSION;
break;
case SP_HEROISM:
if (tmp->type != PLAYER)
break;
/* if (tmp->contr->orig_stats.Str > 19)
force->stats.Str = 1;
else
force->stats.Str = 2;
if (tmp->contr->orig_stats.Con > 19)
force->stats.Con = 1;
else if (tmp->contr->orig_stats.Con > 15)
force->stats.Con = 2;
else
force->stats.Con = 3; */
/* peterm: heroism is much more elegantly implemented by
having it cast stat spells for con, str, dex. Repeated
castings will make you more heroic, but only up to a point */
cast_change_attr(op,dir,SP_STRENGTH);
cast_change_attr(op,dir,SP_DEXTERITY);
cast_change_attr(op,dir,SP_CONSTITUTION);
break;
case SP_HOLY_POSSESSION: {
#ifdef MULTIPLE_GODS
int godnr = lookup_god_by_name(determine_god(op));
if(godnr>=0) {
force->attacktype|=Gods[godnr].attacktype;
if(strcmp(Gods[godnr].enemy_race,"none"))
force->slaying = add_string(Gods[godnr].enemy_race);
if(Gods[godnr].immune) force->immune|=Gods[godnr].immune;
if(Gods[godnr].protected) force->protected|=Gods[godnr].protected;
if(Gods[godnr].path_attuned) force->path_attuned|=Gods[godnr].path_attuned;
new_draw_info_format(NDI_UNIQUE, 0,tmp,
"You become possessed by the spirit of %s!",Gods[godnr].name);
} else
new_draw_info(NDI_UNIQUE, 0,op,"Your blessing seems empty.");
#endif
if(tmp!=op) new_draw_info_format(NDI_UNIQUE, 0,tmp,
"You bless %s mightily!",tmp->name);
force->stats.wc += SP_level_dam_adjust(op, SP_HOLY_POSSESSION);
force->stats.ac += SP_level_dam_adjust(op, SP_HOLY_POSSESSION);
break; }
case SP_REGENERATION:
force->stats.hp = 1 + SP_level_dam_adjust(op, SP_REGENERATION);
break;
case SP_CURSE: {
#ifdef MULTIPLE_GODS
int godnr = lookup_god_by_name(determine_god(op));
if(godnr>=0) {
if(Gods[godnr].path_repelled)
force->path_repelled|=Gods[godnr].path_repelled;
if(Gods[godnr].path_denied)
force->path_denied|=Gods[godnr].path_denied;
new_draw_info_format(NDI_UNIQUE, 0,tmp,
"You are a victim of %s's curse!",Gods[godnr].name);
} else
new_draw_info(NDI_UNIQUE, 0,op,"Your curse seems empty.");
#endif
if(tmp!=op) new_draw_info_format(NDI_UNIQUE, 0,tmp,"You curse %s!",tmp->name);
force->stats.ac -= SP_level_dam_adjust(op, SP_CURSE);
force->stats.wc -= SP_level_dam_adjust(op, SP_CURSE);
break; }
case SP_BLESS: {
#ifdef MULTIPLE_GODS
int godnr = lookup_god_by_name(determine_god(op));
if(godnr>=0) {
if(Gods[godnr].protected) force->protected|=Gods[godnr].protected;
if(Gods[godnr].path_attuned) force->path_attuned|=Gods[godnr].path_attuned;
new_draw_info_format(NDI_UNIQUE, 0,tmp,
"You receive the blessing of %s.",Gods[godnr].name);
} else
new_draw_info(NDI_UNIQUE, 0,op,"Your blessing seems empty.");
#endif
if(tmp!=op) new_draw_info_format(NDI_UNIQUE, 0,tmp,"You bless %s.",tmp->name);
force->stats.wc += SP_level_dam_adjust(op, SP_BLESS);
force->stats.ac += SP_level_dam_adjust(op, SP_BLESS);
break; }
case SP_DARK_VISION:
SET_FLAG(force,FLAG_SEE_IN_DARK);
break;
case SP_PROT_COLD:
force->protected|=AT_COLD;
break;
case SP_PROT_FIRE:
force->protected|=AT_FIRE;
break;
case SP_PROT_ELEC:
force->protected|=AT_ELECTRICITY;
break;
case SP_PROT_POISON:
force->protected|=AT_POISON;
break;
case SP_PROT_SLOW:
force->protected|=AT_SLOW;
break;
case SP_PROT_PARALYZE:
force->protected|=AT_PARALYZE;
break;
case SP_PROT_DRAIN:
force->protected|=AT_DRAIN;
break;
case SP_PROT_ATTACK:
force->protected|=AT_PHYSICAL;
break;
case SP_PROT_MAGIC:
force->protected|=AT_MAGIC;
break;
case SP_PROT_CONFUSE:
force->protected|=AT_CONFUSION;
break;
case SP_PROT_CANCEL:
force->protected|=AT_CANCELLATION;
break;
case SP_PROT_DEPLETE:
force->protected|=AT_DEPLETE;
break;
case SP_LEVITATE:
SET_FLAG(force, FLAG_FLYING);
break;
/*mlee*/
case SP_IMMUNE_COLD:
force->immune|=AT_COLD;
break;
case SP_IMMUNE_FIRE:
force->immune|=AT_FIRE;
break;
case SP_IMMUNE_ELEC:
force->immune|=AT_ELECTRICITY;
break;
case SP_IMMUNE_POISON:
force->immune|=AT_POISON;
break;
case SP_IMMUNE_SLOW:
force->immune|=AT_SLOW;
break;
case SP_IMMUNE_PARALYZE:
force->immune|=AT_PARALYZE;
break;
case SP_IMMUNE_DRAIN:
force->immune|=AT_DRAIN;
break;
case SP_IMMUNE_ATTACK:
force->immune|=AT_PHYSICAL;
break;
case SP_IMMUNE_MAGIC:
force->immune|=AT_MAGIC;
break;
case SP_INVULNERABILITY:
force->immune|=262143;
break;
case SP_PROTECTION:
force->protected|=262143;
break;
case SP_HASTE:
force->stats.exp=(3+SP_level_dam_adjust(op, SP_HASTE));
if(op->speed > 0.2 * SP_level_strength_adjust(op,SP_HASTE))
force->stats.exp=0;
break;
case SP_XRAY:
SET_FLAG(force,FLAG_XRAYS);
break;
}
force->speed_left= -1-SP_level_strength_adjust(op, spell_type)*0.1;
SET_FLAG(force, FLAG_APPLIED);
force = insert_ob_in_ob(force,tmp);
change_abil(tmp,force); /* Mostly to display any messages */
fix_player(tmp); /* This takes care of some stuff that change_abil() */
/* unfortunately is incapable off. */
if(tmp->type==PLAYER)
draw_stats(tmp);
return 1;
}
#define MAX_PET_MONSTERS 5
char mage_pet_monsters [MAX_PET_MONSTERS][16] =
{"bat","spider","stalker","beholder","dark_elf"};
int mage_num_called [MAX_PET_MONSTERS] = {2,1,1,2,3};
char priest_pet_monsters [MAX_PET_MONSTERS][16] =
{"bee","killer_bee","devil","angel","panther"};
int priest_num_called [MAX_PET_MONSTERS] = {3,2,2,2,5};
char altern_pet_monsters [MAX_PET_MONSTERS][16] =
{"bird","pixie","skeleton","skull","vampire"};
int altern_num_called [MAX_PET_MONSTERS] = {1,1,2,1,1};
/* this pet monster stuff is total crap!
** We should replace it with:
struct summoned_mon int foo {
char * mon_arch;
int num_summoned;
}
struct summoned_mon pets_summoned = {
{ "bird", 5 },
{ "vampire", 6},
{ NULL, 0 } -* terminator *-
}
**
*/
int summon_pet(object *op, int dir, SpellTypeFrom item) {
int level, number, i;
char *monster;
archetype *at;
/*
level = ((op->head?op->head->level:op->level) / 4);
*/
level = ((op->head?op->head->level:SK_level(op)) / 4);
if (level >= MAX_PET_MONSTERS)
level = MAX_PET_MONSTERS - 1;
switch(RANDOM()%3) {
case 0:
number = priest_num_called[level];
monster = priest_pet_monsters[level];
break;
case 1:
number = mage_num_called[level];
monster = mage_pet_monsters[level];
break;
default:
number = altern_num_called[level];
monster = altern_pet_monsters[level];
break;
}
at = find_archetype(monster);
if(at == NULL) {
LOG(llevError,"Unknown archetype in summon pet: %s\n",monster);
return 0;
}
if (!dir)
dir = find_free_spot(at, op->map, op->x, op->y, 1, SIZEOFFREE);
if(arch_blocked(at,op->map, op->x + freearr_x[dir], op->y+freearr_y[dir]))
{
new_draw_info(NDI_UNIQUE, 0,op, "There is something in the way.");
if(op->type == PLAYER)
op->contr->count_left = 0;
return 0;
}
if (item != spellNormal)
/* op->stats.sp -= 5 + 10*level + op->level; */
op->stats.sp -= 5 + 10*level + SK_level(op);
for (i = 1; i < number + 1; i++) {
archetype *atmp;
object *prev = NULL, *head = NULL; /* We want to summon dragons *grin* */
for(atmp = at; atmp!=NULL; atmp = atmp->more) {
object *tmp;
tmp = arch_to_object(atmp);
if (atmp == at) {
set_owner(tmp, op);
SET_FLAG(tmp, FLAG_MONSTER);
if (op->type == PLAYER) {
tmp->stats.exp = 0;
add_friendly_object(tmp);
SET_FLAG(tmp, FLAG_FRIENDLY);
tmp->move_type = PETMOVE;
} else
if(QUERY_FLAG(op, FLAG_FRIENDLY)) {
add_friendly_object(tmp);
SET_FLAG(tmp, FLAG_FRIENDLY);
tmp->move_type = PETMOVE;
} else
tmp->speed_left = -1;
tmp->enemy = op->enemy;
tmp->type = 0;
}
if(head == NULL)
head = tmp;
tmp->x = op->x + freearr_x[dir] + tmp->arch->clone.x;
tmp->y = op->y + freearr_y[dir] + tmp->arch->clone.y;
tmp->map = op->map;
if(head != tmp)
tmp->head = head, prev->more = tmp;
prev = tmp;
}
head->direction = dir;
insert_ob_in_map(head, op->map);
if (!QUERY_FLAG(head, FLAG_FREED) && at->randomitems != NULL) {
object *tmp;
create_treasure(at->randomitems,head,GT_INVENTORY,6,0);
for(tmp = head->inv; tmp != NULL; tmp = tmp->below)
if(!tmp->nrof)
SET_FLAG(tmp, FLAG_NO_DROP);
}
dir = absdir(dir + 1);
if (arch_blocked(at,op->map, op->x + freearr_x[dir],
op->y + freearr_y[dir]))
{
if (i < number) {
new_draw_info(NDI_UNIQUE, 0,op, "There is something in the way,");
new_draw_info(NDI_UNIQUE, 0,op, "no more pets for this casting.");
if (item != spellNormal) {
/* op->stats.sp += (5 + 12 * level + op->level) / (number - i); */
op->stats.sp += (5 + 12 * level + SK_level(op)) / (number - i);
if (op->stats.sp < 0)
op->stats.sp = 0;
}
return 1;
}
}
}
if (item != spellNormal && op->stats.sp < 0)
op->stats.sp = 0;
return 1;
}
int create_bomb(object *op,int dir,char *name) {
object *tmp;
int dx=op->x+freearr_x[dir],dy=op->y+freearr_y[dir];
if(wall(op->map,dx,dy)) {
new_draw_info(NDI_UNIQUE, 0,op,"There is something in the way.");
return 0;
}
tmp=get_archetype(name);
/* level dependencies for bomb */
tmp->stats.dam=SP_PARAMETERS[SP_BOMB].bdam + SP_level_dam_adjust(op,SP_BOMB);
tmp->stats.hp=SP_PARAMETERS[SP_BOMB].bdur + SP_level_strength_adjust(op,SP_BOMB);
set_owner(tmp,op);
tmp->x=dx,tmp->y=dy;
insert_ob_in_map(tmp,op->map);
return 1;
}
void animate_bomb(object *op) {
int i;
object *env;
archetype *at = find_archetype("splint");
if(op->state!=op->arch->animations-1)
return;
for(env=op;env->env!=NULL;env=env->env);
if (op->env) {
if (op->type==PLAYER) drop(env,op);
else {
remove_ob(op);
insert_ob_in_map(op, env->map);
}
}
if (env->map == NULL)
return;
if (at)
for(i=1;i<9;i++)
fire_arch(op,i,at,0,0);
remove_ob(op);
op->x=env->x;
op->y=env->y;
if(!explode_object(op)) /* Boom 8) */
LOG(llevError,"Error: bomb refused to go off.\n");
return;
}
int fire_cancellation(object *op,int dir,archetype *at, int magic) {
object *tmp;
if(at==NULL)
return 0;
tmp=arch_to_object(at);
if(tmp==NULL)
return 0;
tmp->x=op->x,tmp->y=op->y;
tmp->direction=dir;
if(magic)
tmp->attacktype|=AT_MAGIC;
set_owner(tmp,op);
if(op->type==PLAYER)
D_LOCK(op);
insert_ob_in_map(tmp,op->map);
move_cancellation(tmp);
if(op->type==PLAYER)
D_UNLOCK(op);
return 1;
}
void move_cancellation(object *op) {
remove_ob(op);
op->x+=DIRX(op),op->y+=DIRY(op);
if(!op->direction||wall(op->map,op->x,op->y)) {
free_object(op);
return;
}
if(reflwall(op->map,op->x,op->y)) {
op->direction=absdir(op->direction+4);
insert_ob_in_map(op,op->map);
return;
}
hit_map(op, 0, op->attacktype);
insert_ob_in_map(op,op->map);
}
void cancellation(object *op)
{
object *tmp;
if(QUERY_FLAG(op, FLAG_ALIVE)||op->type == CONTAINER) {
/* Recur through the inventory */
for(tmp=op->inv;tmp!=NULL;tmp=tmp->below)
if (!did_make_save_item(tmp, AT_CANCELLATION))
cancellation(tmp);
if(op->type==PLAYER && op->contr->eric_server <= 0) {
draw_inventory(op);
draw_look(op);
fix_player(op); /* Remember that wc/dam has probably changed */
}
}
else /* Nullify this object. */
if(FABS(op->magic)<=(RANDOM()%6)) {
op->magic=0;
CLEAR_FLAG(op, FLAG_DAMNED);
CLEAR_FLAG(op, FLAG_CURSED);
CLEAR_FLAG(op, FLAG_KNOWN_MAGICAL);
CLEAR_FLAG(op, FLAG_KNOWN_CURSED);
if (op->env && op->env->type == PLAYER)
if (op->env->contr->eric_server > 0)
esrv_send_item (op->env, op);
else
draw_inventory(op->env);
}
}
/* Create a missile (nonmagic - magic +4). Will either create bolts or arrows
* based on whether a crossbow or bow is equiped. If neither, it defaults to
* arrows.
* Sets the plus based on the casters level. It is also settable with the
* invoke command. If the caster attempts to create missiles with too
* great a plus, the default is used.
* The # of arrows create also goes up with level, so if a 30th level mage
* wants LOTS of arrows, and doesn't care what the plus is he could
* create nonnmagic arrows, or even -1, etc...
*
* Written by Ben Fennema (huma@netcom.com)
*/
int cast_create_missile(object *op, int dir, char *stringarg)
{
int missile_plus=0, missile_nrof;
char *missile_name = NULL;
object *tmp, *missile=NULL, *weap=NULL;
for (tmp=op->inv; tmp != NULL; tmp=tmp->below)
if (tmp->type == BOW && QUERY_FLAG(tmp, FLAG_APPLIED))
weap= tmp;
if (weap==NULL) missile_name = "arrow";
else if (!strcmp(weap->race,"arrows")) missile_name="arrow";
else missile_name = "bolt";
if (stringarg)
missile_plus = atoi(stringarg);
if (!stringarg || ((1 + SP_level_strength_adjust(op, SP_CREATE_MISSILE)) -
(3 * missile_plus)) < 0)
missile_plus = SP_PARAMETERS[SP_CREATE_MISSILE].bdam +
SP_level_dam_adjust(op, SP_CREATE_MISSILE);
if (missile_plus > 4)
missile_plus = 4;
else if (missile_plus < -4)
missile_plus = -4;
missile_nrof = SP_PARAMETERS[SP_CREATE_MISSILE].bdur *
((1 + SP_level_strength_adjust(op, SP_CREATE_MISSILE)) -
(3 * missile_plus));
if (find_archetype(missile_name)==NULL) {
LOG(llevDebug, "Cast create_missile: could not find archtype %s\n", missile_name);
return 0;
}
missile = get_archetype(missile_name);
missile->nrof = missile_nrof;
missile->magic = missile_plus;
missile->value=0;
SET_FLAG(missile, FLAG_IDENTIFIED);
if (!cast_create_obj(op,missile,dir) && op->type==PLAYER)
pick_up(op, missile);
return 1;
}
/* Alchemy code by Mark Wedel (master@rahul.net)
*
* This code adds a new spell, called alchemy. Alchemy will turn
* objects to gold nuggets, the value of the gold nuggets being
* about 90% of that of the item itself. It uses the value of the
* object before charisma adjustments, because the nuggets themselves
* will be will be adjusted by charisma when sold.
*
* Large nuggets are worth 25 gp each (base). You will always get
* the maximum number of large nuggets you could get.
* Small nuggets are worth 1 gp each (base). You will get from 0
* to the max amount of small nuggets as you could get.
*
* For example, if an item is worth 110 gold, you will get
* 4 large nuggets, and from 0-10 small nuggets.
*
* There is also a chance (1:30) that you will get nothing at all
* for the object. There is also a maximum weight that will be
* alchemied.
*/
/* I didn't feel like passing these as arguements to the
* two functions that need them. Real values are put in them
* when the spell is cast, and these are freed when the spell
* is finished.
*/
static object *small, *large;
static void alchemy_object(object *obj, int *small_nuggets,
int *large_nuggets, int *weight)
{
int value=query_cost(obj, NULL, F_TRUE);
/* Give half price when we alchemy money (This should hopefully
* make it so that it isn't worth it to alchemy money, sell
* the nuggets, alchemy the gold from that, etc.
* Otherwise, give 9 silver on the gold for other objects,
* so that it would still be more affordable to haul
* the stuff back to town.
*/
if (QUERY_FLAG(obj, FLAG_UNPAID))
value=0;
else if (obj->type==MONEY || obj->type==GEM)
value /=3;
else if (QUERY_FLAG(obj,FLAG_UNPAID)) value=0;
else
value *= 0.9;
if ((obj->value>0) && RANDOM()%30) {
#ifdef LOSSY_ALCHEMY
int tmp = (value % large->value) / small->value;
*large_nuggets += value/ large->value;
if (tmp)
*small_nuggets += RANDOM() % (tmp + 1);
#else
static int value_store;
int count;
value_store += value;
count = value_store / large->value;
*large_nuggets += count;
value_store -= count * large->value;
count = value_store / small->value;
*small_nuggets += count;
value_store -= count * small->value;
/* LOG(llevDebug, "alchemize value %d, remainder %d\n", value, value_store); */
#endif
}
/* Turn 25 small nuggets into 1 large nugget. If the value
* of large nuggets is not evenly divisable my the small nugget
* value, take off an extra small_nugget (Assuming small_nuggets!=0)
*/
if (*small_nuggets * small->value >= large->value) {
(*large_nuggets)++;
*small_nuggets -= large->value / small->value;
if (*small_nuggets && large->value % small->value)
(*small_nuggets)--;
}
weight += obj->weight;
remove_ob(obj);
free_object(obj);
}
static void update_map(object *op, int small_nuggets, int large_nuggets,
int x, int y)
{
object *tmp;
if (small_nuggets) {
tmp = get_object();
copy_object(small, tmp);
tmp-> nrof = small_nuggets;
tmp->x = x;
tmp->y = y;
insert_ob_in_map(tmp, op->map);
}
if (large_nuggets) {
tmp = get_object();
copy_object(large, tmp);
tmp-> nrof = large_nuggets;
tmp->x = x;
tmp->y = y;
insert_ob_in_map(tmp, op->map);
}
}
int alchemy(object *op)
{
int x,y,weight=0,weight_max,large_nuggets,small_nuggets;
object *next,*tmp;
if(op->type!=PLAYER)
return 0;
/* Put a maximum weight of items that can be alchemied. Limits the power
* some, and also prevents people from alcheming every table/chair/clock
* in sight
*/
/* weight_max = 100000 + 50000*op->level; */
weight_max = 100000 + 50000*SK_level(op);
small=get_archetype("smallnugget"),
large=get_archetype("largenugget");
for(y= op->y-1;y<=op->y+1;y++) {
for(x= op->x-1;x<=op->x+1;x++) {
if(out_of_map(op->map,x,y) || wall(op->map,x,y) ||
blocks_view(op->map,x,y))
continue;
small_nuggets=0;
large_nuggets=0;
for(tmp=get_map_ob(op->map,x,y);tmp!=NULL;tmp=next) {
next=tmp->above;
if (tmp->weight>0 && !QUERY_FLAG(tmp, FLAG_NO_PICK)
&& !QUERY_FLAG(tmp, FLAG_ALIVE)) {
if(QUERY_FLAG(tmp,FLAG_IS_CAULDRON)) {
#ifdef ALCHEMY
attempt_do_alchemy(op, tmp);
#endif
continue;
}
if (tmp->inv) {
object *next1,*tmp1;
for (tmp1 = tmp->inv; tmp1!=NULL; tmp1=next1) {
next1 = tmp1->below;
if (tmp1->weight>0 && !QUERY_FLAG(tmp1, FLAG_NO_PICK) && !QUERY_FLAG(tmp1, FLAG_ALIVE))
alchemy_object(tmp1, &small_nuggets, &large_nuggets,
&weight);
}
}
alchemy_object(tmp, &small_nuggets, &large_nuggets, &weight);
if (weight>weight_max) {
update_map(op, small_nuggets, large_nuggets, x, y);
free_object(large);
free_object(small);
return 1;
}
}
}
/* Insert all the nuggets at one time. This probably saves time, but
* it also prevents us from alcheming nuggets that were just created
* with this spell.
*/
update_map(op, small_nuggets, large_nuggets, x, y);
}
}
free_object(large);
free_object(small);
return 1;
}
int remove_curse(object *op, int type, SpellTypeFrom src) {
object *tmp;
int success = 0, was_one = 0;
for (tmp = op->inv; tmp; tmp = tmp->below)
if (QUERY_FLAG(tmp, FLAG_APPLIED) && (QUERY_FLAG(tmp, FLAG_CURSED) ||
(type == SP_REMOVE_DAMNATION && QUERY_FLAG(tmp, FLAG_DAMNED))))
{
was_one++;
/* if (tmp->level <= op->level) */
if (tmp->level <= SK_level(op))
{
success++;
if (type == SP_REMOVE_DAMNATION)
CLEAR_FLAG(tmp, FLAG_DAMNED);
CLEAR_FLAG(tmp, FLAG_CURSED);
CLEAR_FLAG(tmp, FLAG_KNOWN_CURSED);
tmp->value = 0; /* Still can't sell it */
if (op->type == PLAYER && op->contr->eric_server > 0)
esrv_send_item(op, tmp);
}
}
if (op->type==PLAYER) {
if (success) {
new_draw_info(NDI_UNIQUE, 0,op, "You feel like someone is helping you.");
draw_inventory(op);
} else
if (was_one)
new_draw_info(NDI_UNIQUE, 0,op, "You failed to remove the curse.");
else if (src == spellNormal)
new_draw_info(NDI_UNIQUE, 0,op, "You are not using any cursed items.");
else
new_draw_info(NDI_UNIQUE, 0,op, "You hear manical laughter in the distance.");
}
return success;
}
int cast_identify(object *op) {
object *tmp;
int success = 0, random_val=0;
int chance = 8 + op->stats.luck + op->stats.Wis;
if (chance < 1)
chance = 1;
for (tmp = op->inv; tmp ; tmp = tmp->below)
if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) && !tmp->invisible &&
need_identify(tmp))
{
identify(tmp);
if (op->type==PLAYER) {
new_draw_info_format(NDI_UNIQUE, 0, op,
"You have %s.", long_desc(tmp));
if (tmp->msg) {
new_draw_info(NDI_UNIQUE, 0,op, "The item has a story:");
new_draw_info(NDI_UNIQUE, 0,op, tmp->msg);
}
if (op->contr->eric_server > 0)
esrv_send_item(op, tmp);
}
if ((random_val=RANDOM()%chance) > (chance - ++success - 2))
break;
}
/* If all the power of the spell has been used up, don't go and identify
* stuff on the floor. Only identify stuff on the floor if the spell
* was not fully used.
*/
if (random_val<=chance - success -2) {
for(tmp = get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above)
if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) && !tmp->invisible &&
need_identify(tmp))
{
identify(tmp);
if (op->type==PLAYER) {
new_draw_info_format(NDI_UNIQUE, 0,op,
"On the ground is %s.", long_desc(tmp));
if (tmp->msg) {
new_draw_info(NDI_UNIQUE, 0,op, "The item has a story:");
new_draw_info(NDI_UNIQUE, 0,op, tmp->msg);
}
if (op->contr->eric_server > 0)
esrv_send_item(op, tmp);
}
if (RANDOM() %chance > (chance - ++success - 2))
break;
}
}
if (!success)
new_draw_info(NDI_UNIQUE, 0,op, "You can't reach anything unidentified.");
else {
spell_effect(SP_IDENTIFY, op->x, op->y, op->map);
if (op->type == PLAYER && op->contr->eric_server <= 0)
draw_inventory(op);
}
return success;
}
int cast_detection(object *op, int type) {
object *tmp;
int x,y,done_one;
archetype *detect_arch;
detect_arch = find_archetype("detect_magic");
if (detect_arch == (archetype *) NULL)
{
LOG(llevError, "Couldn't find archetype detect_magic.\n");
return 0;
}
for (x = op->x + WINLEFT; x <= op->x + WINRIGHT; x++)
for (y = op->y + WINUPPER; y <= op->y + WINLOWER; y++) {
if (out_of_map(op->map, x, y))
continue;
done_one = 0;
for (tmp = get_map_ob(op->map, x, y); tmp &&
(!done_one || type==SP_DETECT_MAGIC || type==SP_DETECT_CURSE);
tmp = tmp->above)
{
switch(type) {
case SP_DETECT_MAGIC:
if (!QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL) && !QUERY_FLAG(tmp, FLAG_IDENTIFIED) &&
(is_magical(tmp) || always_magical(tmp))) {
SET_FLAG(tmp,FLAG_KNOWN_MAGICAL);
if(tmp->type==RUNE) /*peterm: make runes more visible*/
if(tmp->attacktype&AT_MAGIC) /* if they're magic! */
tmp->stats.Cha/=4;
done_one = 1;
}
break;
case SP_DETECT_MONSTER:
if (op->type == PLAYER)
done_one = QUERY_FLAG(tmp, FLAG_MONSTER);
else
done_one = (tmp->type == PLAYER);
break;
case SP_DETECT_EVIL:
if (op->type == PLAYER)
done_one = (QUERY_FLAG(tmp, FLAG_MONSTER)&&
!QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE)&&
!QUERY_FLAG(tmp, FLAG_FRIENDLY));
else
done_one = (tmp->type == PLAYER);
break;
case SP_SHOW_INVIS:
done_one = tmp->invisible;
/* if(RANDOM()%(op->level) > tmp->level/4) tmp->invisible=0; */
if(RANDOM()%(SK_level(op)) > tmp->level/4) tmp->invisible=0;
break;
case SP_DETECT_CURSE:
if (!QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) &&
(QUERY_FLAG(tmp, FLAG_CURSED) ||
QUERY_FLAG(tmp, FLAG_DAMNED))) {
SET_FLAG(tmp, FLAG_KNOWN_CURSED);
done_one = 1;
}
break;
}
} /* Done all the object on this square */
if (done_one) {
object *detect_ob = arch_to_object(detect_arch);
detect_ob->x = x;
detect_ob->y = y;
insert_ob_in_map(detect_ob, op->map);
}
}
if ((type == SP_DETECT_MAGIC || type == SP_DETECT_CURSE) &&
op->type == PLAYER)
{
done_one = 0;
for (tmp = op->inv; tmp; tmp = tmp->below)
if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED))
switch(type) {
case SP_DETECT_MAGIC:
if (is_magical(tmp) && !QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL)) {
SET_FLAG(tmp,FLAG_KNOWN_MAGICAL);
if (op->contr->eric_server > 0)
esrv_send_item (op, tmp);
done_one = 1;
}
break;
case SP_DETECT_CURSE:
if (!QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) &&
(QUERY_FLAG(tmp, FLAG_CURSED) ||
QUERY_FLAG(tmp, FLAG_DAMNED))) {
SET_FLAG(tmp, FLAG_KNOWN_CURSED);
if (op->contr->eric_server > 0)
esrv_send_item (op, tmp);
done_one = 1;
}
break;
}
if (done_one && op->contr->eric_server <= 0)
draw_inventory(op);
}
return 1;
}
/* Shamelessly hacked from PeterM's cast_charm and destruction code
* - b.t. thomas@nomad.astro.psu.edu
*/
/* Changes in the spell code to make it more powerfull - now it can
* pacify multi-square creatures, and has greater range - Aug 95 b.t.
*/
/* New modification -- w/ Multigod hack, now if its a member of an aligned
* race, we automatically pacify it. b.t.
*/
int cast_pacify(object *op, object *weap, archetype *arch,int spellnum ) {
int i,r,j;
object *tmp,*effect;
#ifdef MULTIPLE_GODS
int godnr = get_god(op);
#endif
r= 1 + SP_level_strength_adjust(op,SP_PACIFY);
for(i= -r;i<r;i++)
for(j= -r;j<r;j++) {
if(out_of_map(op->map,op->x+i,op->y+j))
continue;
for(tmp=get_map_ob(op->map,op->x+i,op->y+j);
tmp&&(!QUERY_FLAG(tmp,FLAG_MONSTER));tmp=tmp->above);
if(!tmp) continue;
if(tmp->type==PLAYER) continue;
#ifdef MULTIPLE_GODS /* we only go through checking if the monster is not aligned
member, we dont worship a god, or monster has no race */
if(!tmp->race||godnr==-1
||!strstr(Gods[godnr].aligned_race,tmp->race)) {
#endif
if(tmp->immune&AT_MAGIC||tmp->immune&AT_GODPOWER) continue;
/* multiple square monsters only when caster is => level of creature */
if((tmp->more || tmp->head) && (SK_level(op) < tmp->level)) continue;
if(weap->slaying) /* selective pacify */
if(tmp->race != weap->slaying && tmp->name != weap->slaying) continue;
/* if(op->level <( (RANDOM()%(2*tmp->level+1))-(op->stats.Cha-10)/2)) continue; */
if(SK_level(op) <( (RANDOM()%(2*tmp->level+1))-(op->stats.Cha-10)/2)) continue;
#ifdef MULTIPLE_GODS
}
#endif
if((effect=get_archetype("detect_magic"))){
effect->x = tmp->x;
effect->y = tmp->y;
insert_ob_in_map(effect,tmp->map);
}
SET_FLAG(tmp,FLAG_UNAGGRESSIVE);
}
return 1;
}
/* summon fog code. I could'nt decide whether this
* could just go into another routine (like create_
* the_feature) or have it alone. For now, its separate
* function. This code just creates a variable amount of
* fog archetypes around the character.
* Implementation by b.t. (thomas@nomad.astro.psu.edu)
* (based on create bomb code)
*/
int summon_fog(object *op, int dir,int spellnum) {
object *tmp;
int i,dx=op->x+freearr_x[dir],dy=op->y+freearr_y[dir];
if (!spellarch[spellnum])
return 0;
for(i=1;i<MIN(2+SP_level_strength_adjust(op,spellnum),SIZEOFFREE);i++) {
if(wall(op->map,dx,dy)) {
new_draw_info(NDI_UNIQUE, 0,op,"There is something in the way.");
return 0;
}
tmp=get_archetype(spellarch[spellnum]->name);
tmp->x=dx,tmp->y=dy; /* all fog starts in 1 place */
#ifdef WALL_CREDIT /* does someone get exp for the kills? */
set_owner(tmp,op); /* note however, that after 'fog' moves */
/* it is no longer owned. It is unlikely */
/* that players will garner much exp with */
/* this spell */
#endif
insert_ob_in_map(tmp,op->map);
}
return 1;
}
/* create_the_feature: peterm */
/* implementation of the spells which build directors, lightning
walls, bullet walls, and fireballwalls. */
int create_the_feature(object *op, int dir, int spell_effect)
{
object *tmp=NULL;
char buf1[20];
int putflag=0;
if(!dir) dir=op->facing; else putflag=1;
switch(spell_effect) {
case SP_BUILD_DIRECTOR:
sprintf(buf1,"director_%d",dir);
tmp=get_archetype(buf1);
SET_FLAG(tmp, FLAG_IS_USED_UP);
tmp->stats.food=SP_PARAMETERS[spell_effect].bdur+10*SP_level_strength_adjust(op,spell_effect);
tmp->stats.hp=SP_PARAMETERS[spell_effect].bdam+5*SP_level_dam_adjust(op,spell_effect);
tmp->stats.maxhp=tmp->stats.hp;
break;
case SP_BUILD_LWALL:
sprintf(buf1,"lightningwall_%d",dir);
tmp=get_archetype(buf1);
SET_FLAG(tmp, FLAG_IS_USED_UP);
SET_FLAG(tmp, FLAG_TEAR_DOWN);
SET_FLAG(tmp, FLAG_ALIVE);
tmp->stats.food=SP_PARAMETERS[spell_effect].bdur+10*SP_level_strength_adjust(op,spell_effect);
tmp->stats.hp=SP_PARAMETERS[spell_effect].bdam+5*SP_level_dam_adjust(op,spell_effect);
tmp->stats.maxhp=tmp->stats.hp;
break;
case SP_BUILD_BWALL:
sprintf(buf1,"lbulletwall_%d",dir);
tmp=get_archetype(buf1);
SET_FLAG(tmp, FLAG_IS_USED_UP);
SET_FLAG(tmp, FLAG_TEAR_DOWN);
SET_FLAG(tmp, FLAG_ALIVE);
tmp->stats.food=SP_PARAMETERS[spell_effect].bdur+10*SP_level_strength_adjust(op,spell_effect);
tmp->stats.hp=SP_PARAMETERS[spell_effect].bdam+5*SP_level_dam_adjust(op,spell_effect);
tmp->stats.maxhp=tmp->stats.hp;
break;
case SP_BUILD_FWALL:
sprintf(buf1,"firewall_%d",dir);
tmp=get_archetype(buf1);
SET_FLAG(tmp, FLAG_IS_USED_UP);
SET_FLAG(tmp, FLAG_TEAR_DOWN);
SET_FLAG(tmp, FLAG_ALIVE);
tmp->stats.food=SP_PARAMETERS[spell_effect].bdur+10*SP_level_strength_adjust(op,spell_effect);
tmp->stats.hp=SP_PARAMETERS[spell_effect].bdam+5*SP_level_dam_adjust(op,spell_effect);
tmp->stats.maxhp=tmp->stats.hp;
break;
}
#ifdef WALL_CREDIT
set_owner(tmp,op);
#endif /* determines whether someone gets credit for kills. */
tmp->level=SK_level(op)/2; /* so that the spell that the wall casts
inherit part of the effectiveness of
of the wall builder */
tmp->x=op->x;tmp->y=op->y;
if(putflag) { tmp->x+=freearr_x[dir];tmp->y+=freearr_y[dir];}
insert_ob_in_map(tmp,op->map);
if(QUERY_FLAG(tmp, FLAG_BLOCKSVIEW))
update_all_los(op->map);
if(op->type==PLAYER)
draw(op);
else
SET_FLAG(op, FLAG_SCARED); /* We don't want them to walk through the wall! */
return 1;
}
/* cast_transfer: peterm */
/* following spell transfers mana from one person to another.
right now, it's no respecter of maximum sp limits. WOn't
fix that, regard it as a feature. be nice to make someone's
head explode if they supercharge too much, though. */
int cast_transfer(object *op,int dir) {
object *plyr;
/* see if we can find someone to give sp to. */
for(plyr=get_map_ob(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir]);
plyr!=NULL;
plyr=plyr->above)
if(QUERY_FLAG(plyr, FLAG_ALIVE))
break;
/* If we did not find a player in the specified direction, transfer
to anyone on top of us. */
if(plyr==NULL)
for(plyr=get_map_ob(op->map,op->x,op->y); plyr!=NULL; plyr=plyr->above)
if(QUERY_FLAG(plyr,FLAG_ALIVE))
break;
if(plyr) {
int maxsp=plyr->stats.maxsp;
int sp=(plyr->stats.sp+=8);
new_draw_info(NDI_UNIQUE, 0,plyr,"You feel energy course through you.");
if(sp>=maxsp*2) {
new_draw_info(NDI_UNIQUE, 0,plyr,"Your head explodes!");
fire_arch (plyr, 0, spellarch[SP_L_FIREBALL], SP_L_FIREBALL, 0);
/* Explodes a large fireball centered at player */
/* hit_player(plyr, 9999, op, AT_PHYSICAL);*/
plyr->stats.sp = 2*maxsp;
}
else if(sp>=maxsp*1.88)
new_draw_info(NDI_UNIQUE, NDI_ORANGE,plyr,"You feel like your head is going to explode.");
else if(sp>=maxsp*1.66)
new_draw_info(NDI_UNIQUE, 0,plyr, "You get a splitting headache!");
else if(sp>=maxsp*1.5) {
new_draw_info(NDI_UNIQUE, 0,plyr,"CHaOs fills your world.");
confuse_player(op,op,99);
}
else if(sp>=maxsp*1.25)
new_draw_info(NDI_UNIQUE, 0,plyr,"You start hearing voices.");
return 1;
}
else return 0;
}
/* drain_magic: peterm */
/* drains all the magic out of the victim. */
int drain_magic(object *op,int dir) {
object *tmp;
for(tmp=get_map_ob(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir]);
tmp!=NULL;
tmp=tmp->above)
if(QUERY_FLAG(tmp, FLAG_ALIVE))
break;
/* If we did not find a player in the specified direction, transfer
to anyone on top of us. */
if(tmp==NULL)
for(tmp=get_map_ob(op->map,op->x,op->y); tmp!=NULL; tmp=tmp->above)
if(QUERY_FLAG(tmp, FLAG_ALIVE))
break;
if(tmp&&op!=tmp) { tmp->stats.sp*=0.1; return 1; }
else return 0;
}
/* counterspell: peterm */
/* an object of type counterspell will nullify cone objects,
explosion objects, and anything else that |=magic. */
void counterspell(object *op)
{
object *tmp;
int nflag=0;
for(tmp=get_map_ob(op->map,op->x,op->y); tmp!=NULL; tmp=tmp->above,nflag=0)
{ if(tmp->material==0 && tmp->attacktype&AT_MAGIC &&!
(tmp->attacktype&AT_COUNTERSPELL)) nflag=1;
else
switch(tmp->type) {
case CONE: case FBALL: case LIGHTNING:
case FBULLET: case MMISSILE: case SPEEDBALL:
case BOMB: case POISONCLOUD: case CANCELLATION:
case SWARM_SPELL:
case BALL_LIGHTNING:
nflag=1;
break;
case RUNE:
nflag=2;
break;
default:
nflag=0;
}
switch(nflag) {
case 1:
/* { if(op->level > tmp->level) { */
{ if(SK_level(op) > tmp->level) {
remove_ob(tmp);
free_object(tmp);
}
break;
}
case 2:
{ if(RANDOM()%150 == 0) {
tmp->stats.hp--; /* weaken the rune */
if(!tmp->stats.hp) {
remove_ob(tmp);
free_object(tmp);
}
}
break;
}
}
}
}
/* peterm: function which summons hostile monsters and
places them in nearby squares. */
int summon_hostile_monsters(object *op,int n,char *monstername){
int i;
for(i=0;i<n;i++)
put_a_monster(op,monstername);
return n;
}
/* charm spell by peterm@soda.berkeley.edu
searches nearby squares for monsters to charm. Each of them
is subject to being charmed, that is, to becoming the pet
monster of the caster. Monsters larger than 1 square
are uncharmeable right now. */
/* Aug 95 - hack on the code to make it charm only undead for
* priests, and not charm undead for magicians -b.t
*/
int cast_charm(object *op, archetype *arch,int spellnum) {
int i;
object *tmp,*effect;
for(i=1;i<MIN(9+SP_level_strength_adjust(op,spellnum),SIZEOFFREE);i++) {
for(tmp=get_map_ob(op->map,op->x+freearr_x[i],op->y+freearr_y[i]);
tmp&&(!QUERY_FLAG(tmp,FLAG_MONSTER));tmp=tmp->above);
if(!tmp) continue;
if(tmp->type==PLAYER) continue;
if(tmp->immune & AT_MAGIC) continue;
if(QUERY_FLAG(tmp,FLAG_UNDEAD)) continue;
if(tmp->more || tmp->head) continue; /* multiple square monsters NOT */
/* if(op->level <( (RANDOM()%(2*tmp->level+1))-(op->stats.Cha-10)/2)) continue; */
if(SK_level(op) <( (RANDOM()%(2*tmp->level+1))-(op->stats.Cha-10)/2)) continue;
if((effect=get_archetype("detect_magic"))){
effect->x = tmp->x;
effect->y = tmp->y;
insert_ob_in_map(effect,tmp->map);
}
set_owner(tmp,op);
SET_FLAG(tmp,FLAG_MONSTER);
if(op->type==PLAYER)
tmp->stats.exp = 0;
SET_FLAG(tmp,FLAG_FRIENDLY);
tmp->move_type = PETMOVE;
}
return 1;
}
int cast_charm_undead(object *op, archetype *arch,int spellnum) {
int i;
object *tmp,*effect;
for(i=1;i<MIN(9+SP_level_strength_adjust(op,spellnum),SIZEOFFREE);i++) {
for(tmp=get_map_ob(op->map,op->x+freearr_x[i],op->y+freearr_y[i]);
tmp&&(!QUERY_FLAG(tmp,FLAG_MONSTER));tmp=tmp->above);
if(!tmp) continue;
if(tmp->type==PLAYER) continue;
if(tmp->immune & AT_MAGIC) continue;
if(!QUERY_FLAG(tmp,FLAG_UNDEAD)) continue;
if(tmp->more || tmp->head) continue; /* multiple square monsters NOT */
if(SK_level(op) <( (RANDOM()%(2*tmp->level+1))-(op->stats.Cha-10)/2)) continue;
if((effect=get_archetype("detect_magic"))){
effect->x = tmp->x;
effect->y = tmp->y;
insert_ob_in_map(effect,tmp->map);
}
set_owner(tmp,op);
SET_FLAG(tmp,FLAG_MONSTER);
if(op->type==PLAYER)
tmp->stats.exp = 0;
SET_FLAG(tmp,FLAG_FRIENDLY);
tmp->move_type = PETMOVE;
}
return 1;
}
int summon_cult_monsters(object *op, int dir) {
int godnr=get_god(op), raceindex=0, monindex=0, summon_level, number, i;
int racenr=0;
char *race,*monster,buf[MAX_BUF];
archetype *at = NULL;
/* find deity */
if (godnr<0) {
new_draw_info(NDI_UNIQUE, 0,op, "You worship no living deity!");
return 0;
} else if(!strcmp("none",Gods[godnr].aligned_race)) {
new_draw_info(NDI_UNIQUE, 0,op, "Your deity has no creatures that you may summon!");
return 0;
}
/* now determine the number of races available */
sprintf(buf,Gods[godnr].aligned_race);
race = strtok(buf,",");
while(race) {
racenr++;
race = strtok(NULL,",");
}
/* next, randomly select a race from the aligned_races string */
if(racenr>1) {
racenr = RANDOM()%racenr;
sprintf(buf,Gods[godnr].aligned_race);
race = strtok(buf,",");
for(i=0;i<racenr;i++)
race = strtok(NULL,",");
} else
race = Gods[godnr].aligned_race;
/* see if a we can match a race list of monsters */
for(raceindex=0;raceindex<MAX_RACES;raceindex++)
if(!strcmp(race,summoned_monsters[raceindex][0])) break;
/* no monsters to summon, goodbye!, this is a safety, shouldnt happen */
if(!race||summoned_monsters[monindex][0]=="none") {
LOG(llevDebug,"summon_cult_monster() - failed to find requested aligned race\n");
new_draw_info(NDI_UNIQUE, 0,op,
"The spell fails! Your god's creatures are beyond");
new_draw_info(NDI_UNIQUE, 0,op,
"the range of your summons.");
return 0;
}
summon_level = RANDOM()%(SK_level(op)+((op->stats.Wis?op->stats.Wis:20)/4));
if(op->path_attuned&PATH_SUMMON) summon_level += 5;
if(op->path_repelled&PATH_SUMMON) summon_level -= 5;
LOG(llevDebug,"summon_cult_mon(): got summon level = %d\n",summon_level);
for(monindex=(MAX_RACE_MEMBERS-1);monindex>0;monindex--) {
if(!strcmp((monster = summoned_monsters[raceindex][monindex]),"none")) continue;
if((at = find_archetype(monster))==NULL) continue;
if((at->clone.level<=summon_level)&&!(RANDOM()%2)) break;
}
LOG(llevDebug," monster level = %d\n",at->clone.level);
if(!at||monindex<1||(summon_level<at->clone.level)) {
new_draw_info(NDI_UNIQUE, 0,op, "Your deity fails to send anything.");
return 0;
}
if(at->clone.level>(summon_level/2))
number = RANDOM()%2 + 1;
else
number = RANDOM()%2 + RANDOM()%2 + 2;
if (!dir)
dir = find_free_spot(at, op->map, op->x, op->y, 1, SIZEOFFREE);
if(arch_blocked(at,op->map, op->x + freearr_x[dir], op->y+freearr_y[dir]))
{
new_draw_info(NDI_UNIQUE, 0,op, "There is something in the way.");
if(op->type == PLAYER)
op->contr->count_left = 0;
return 0;
}
for (i = 1; i < number + 1; i++) {
archetype *atmp;
object *prev = NULL, *head = NULL; /* We want to summon dragons *grin* */
for(atmp = at; atmp!=NULL; atmp = atmp->more) {
object *tmp;
tmp = arch_to_object(atmp);
if (atmp == at) {
set_owner(tmp, op);
SET_FLAG(tmp, FLAG_MONSTER);
if (op->type == PLAYER) {
tmp->stats.exp = 0;
add_friendly_object(tmp);
SET_FLAG(tmp, FLAG_FRIENDLY);
tmp->move_type = PETMOVE;
} else
if(QUERY_FLAG(op, FLAG_FRIENDLY)) {
add_friendly_object(tmp);
SET_FLAG(tmp, FLAG_FRIENDLY);
tmp->move_type = PETMOVE;
} else
tmp->speed_left = -1;
tmp->enemy = op->enemy;
tmp->type = 0;
}
if(head == NULL)
head = tmp;
tmp->x = op->x + freearr_x[dir] + tmp->arch->clone.x;
tmp->y = op->y + freearr_y[dir] + tmp->arch->clone.y;
tmp->map = op->map;
if(head != tmp)
tmp->head = head, prev->more = tmp;
prev = tmp;
}
head->direction = dir;
/* need to change some monster attr to prevent crashing */
if(head->attacktype&AT_GHOSTHIT) head->attacktype=(AT_PHYSICAL|AT_DRAIN);
if(head->other_arch) head->other_arch=NULL;
if(QUERY_FLAG(head,FLAG_CHANGING)) CLEAR_FLAG(head,FLAG_CHANGING);
if(QUERY_FLAG(head,FLAG_STAND_STILL)) CLEAR_FLAG(head,FLAG_STAND_STILL);
if(QUERY_FLAG(head,FLAG_GENERATOR)) CLEAR_FLAG(head,FLAG_GENERATOR);
if(QUERY_FLAG(head,FLAG_SPLITTING)) CLEAR_FLAG(head,FLAG_SPLITTING);
/* now, a little bit of tailoring. If the monster is much lower in
* level than the summon_level, we get a better monster */
if((head->level+5)<summon_level) {
int ii;
char buf[MAX_BUF];
for(ii=summon_level-(head->level)-5;ii>0;ii--) {
switch(RANDOM()%3+1) {
case 1:
head->stats.wc--;
break;
case 2:
head->stats.ac--;
break;
case 3:
head->stats.dam+=3;
break;
default:
break;
}
head->stats.hp+=3;
}
head->stats.maxhp=head->stats.hp;
if(head->name) {
if(summon_level>30+head->level)
sprintf(buf,"Arch %s of %s",head->name,Gods[godnr].name);
else
sprintf(buf,"%s of %s",head->name,Gods[godnr].name);
free_string(head->name);
head->name=add_string(buf);
}
}
insert_ob_in_map(head, op->map);
if (!QUERY_FLAG(head, FLAG_FREED) && at->randomitems != NULL) {
object *tmp;
create_treasure(at->randomitems,head,GT_INVENTORY,6,0);
for(tmp = head->inv; tmp != NULL; tmp = tmp->below)
if(!tmp->nrof)
SET_FLAG(tmp, FLAG_NO_DROP);
}
dir = absdir(dir + 1);
if (arch_blocked(at,op->map, op->x + freearr_x[dir],
op->y + freearr_y[dir]))
{
if (i < number) {
new_draw_info(NDI_UNIQUE, 0,op, "There is something in the way,");
new_draw_info(NDI_UNIQUE, 0,op, "no more monsters for this casting.");
return 1;
}
}
}
return 1;
}
/* summon_avatar() - taken from the code which summons golems. We
* cant use because we need to throw in a few extra's here. b.t.
*/
int summon_avatar(object *op,int dir, archetype *at, int spellnum) {
object *tmp;
#ifdef MULTIPLE_GODS
char buf[MAX_BUF];
int godnr=lookup_god_by_name(determine_god(op));
if(godnr==-1) {
new_draw_info(NDI_UNIQUE, 0,op,
"You can't summon if you don't worship a god!");
return 0;
}
#endif
if(op->type==PLAYER)
if(op->contr->golem!=NULL&&!QUERY_FLAG(op->contr->golem,FLAG_FREED)) {
control_golem(op->contr->golem,dir);
return 0;
}
if(!dir)
dir=find_free_spot(NULL,op->map,op->x,op->y,1,9);
if(blocked(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir])) {
new_draw_info(NDI_UNIQUE, 0,op,"There is something in the way.");
if(op->type==PLAYER)
op->contr->count_left=0;
return 0;
}
tmp=arch_to_object(at);
if(op->type==PLAYER) {
CLEAR_FLAG(tmp, FLAG_MONSTER);
tmp->stats.exp=0;
add_friendly_object(tmp);
tmp->type=GOLEM;
set_owner(tmp,op);
op->contr->golem=tmp;
/* give the player control of the golem */
op->contr->shoottype=range_scroll;
} else {
if(QUERY_FLAG(op, FLAG_FRIENDLY)) {
object *owner = get_owner(op);
if(owner != NULL) /* For now, we transfer ownership */
set_owner(tmp,owner);
tmp->move_type = PETMOVE;
add_friendly_object(tmp);
SET_FLAG(tmp, FLAG_FRIENDLY);
}
SET_FLAG(tmp, FLAG_MONSTER);
}
/* This sets the level dependencies on dam, wc and hp */
tmp->stats.wc -= SP_level_strength_adjust(op,spellnum);
if(tmp->stats.wc<-127) tmp->stats.wc = -127;
tmp->stats.hp = SP_PARAMETERS[spellnum].bdur +
12 * SP_level_strength_adjust(op,spellnum);
tmp->stats.dam= SP_PARAMETERS[spellnum].bdam +(2*SP_level_dam_adjust(op,spellnum));
if(tmp->stats.dam<0) tmp->stats.dam=127; /*seen this go negative!*/
#ifdef MULTIPLE_GODS /* tailor it to the gods nature */
sprintf(buf,"%s of %s",tmp->name,Gods[godnr].name);
if(tmp->name) free_string(tmp->name);
tmp->name = add_string(buf);
tmp->attacktype|=Gods[godnr].attacktype;
if(Gods[godnr].vulnerable) tmp->vulnerable|=Gods[godnr].vulnerable;
if(Gods[godnr].immune) tmp->immune|=Gods[godnr].immune;
if(Gods[godnr].protected) tmp->protected|=Gods[godnr].protected;
if(strcmp(Gods[godnr].enemy_race,"none"))
tmp->slaying = add_string(Gods[godnr].enemy_race);
#else
tmp->attacktype|=AT_WEAPONMAGIC;
#endif
/* make experience increase in proportion to the strength of
* the summoned creature. */
tmp->stats.exp *= SP_level_spellpoint_cost(op,spellnum)/spells[spellnum].sp;
tmp->speed_left= -1;
tmp->x=op->x+freearr_x[dir],tmp->y=op->y+freearr_y[dir];
tmp->direction=dir;
insert_ob_in_map(tmp,op->map);
return 1;
}
/* cast_consecrate() - a spell to make an altar your god's */
int cast_consecrate(object *op) {
char buf[MAX_BUF];
int success=0;
#ifdef MULTIPLE_GODS
object *tmp;
int godnr=lookup_god_by_name(determine_god(op));
#endif
sprintf(buf,"Nothing happens.");
#ifdef MULTIPLE_GODS
if(godnr==-1) {
new_draw_info(NDI_UNIQUE, 0,op,
"You can't consecrate anything if you don't worship a god!");
return 0;
}
sprintf(buf,"You are'nt standing over an altar!");
for(tmp=op->below;tmp;tmp=tmp->below)
if(tmp->type==ALTAR) {
if(!tmp->title && strcmp(tmp->name,tmp->arch->clone.name)) {
sprintf(buf,"That altar may not be consecrated.");
break;
} else if(QUERY_FLAG(tmp,FLAG_IS_FLOOR)) break;
if(tmp->title)
free_string(tmp->title);
if(tmp->name)
free_string(tmp->name);
sprintf(buf,"%s of %s",tmp->arch->clone.name,Gods[godnr].name);
tmp->name = add_string(buf);
tmp->title = add_string(Gods[godnr].name);
sprintf(buf,"You consecrated the altar to %s!",Gods[godnr].name);
success = 1;
if(op->type==PLAYER) draw_look(op);
break;
}
#endif
new_draw_info(NDI_UNIQUE, 0,op,buf);
return success;
}
/* finger_of_death() - boss high-level cleric spell. */
int finger_of_death(object *caster, int dir) {
object *hitter,*target=get_pointed_target(caster,dir);
int success = 1;
if(!target) {
new_draw_info(NDI_UNIQUE,0,caster,"Nothing happens.");
return 0;
}
/* we create a hitter object -- the spell */
hitter=get_archetype("face_of_death");
hitter->level=SP_PARAMETERS[SP_FINGER_DEATH].bdam +
3*SP_level_dam_adjust(caster,SP_FINGER_DEATH);
if(caster->path_attuned&PATH_DEATH) hitter->level+=5;
if(caster->path_repelled&PATH_DEATH) hitter->level-=5;
set_owner(hitter,caster);
hitter->x=target->x;
hitter->y=target->y;
hitter->stats.maxhp=hitter->count; /*??*/
/* there are 'grave' consequences for using this spell on the unliving! */
if(QUERY_FLAG(target,FLAG_UNDEAD)) {
success = 0;
if(RANDOM()%3) {
new_draw_info(NDI_UNIQUE,0,caster,"Idiot! Your spell boomerangs!");
hitter->x=caster->x;
hitter->y=caster->y;
} else {
new_draw_info_format(NDI_UNIQUE,0,caster,"The %s looks stronger!",
query_name(target));
target->stats.hp = target->stats.maxhp*2;
free_object(hitter);
return 0;
}
}
insert_ob_in_map(hitter,caster->map);
return success;
}
/* staff_to_snake() - taken from the code which summons golems. We
* allow the cleric to create a golem from thier staff. If the staff
* is magical, the snake created is enhanced, and vice-versa for
* cursed items! -b.t.
*/
int staff_to_snake(object *op,int dir, archetype *at, int spellnum) {
object *weapon, *tmp;
if(op->type==PLAYER)
if(op->contr->golem!=NULL&&!QUERY_FLAG(op->contr->golem,FLAG_FREED)) {
control_golem(op->contr->golem,dir);
return 0;
}
if(!dir)
dir=find_free_spot(NULL,op->map,op->x,op->y,1,9);
if(blocked(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir])) {
new_draw_info(NDI_UNIQUE, 0,op,"There is something in the way.");
if(op->type==PLAYER)
op->contr->count_left=0;
return 0;
}
/* need to find the weapon to transform */
for(weapon=op->inv;weapon;weapon=weapon->below)
if(weapon->type==WEAPON&&QUERY_FLAG(weapon,FLAG_APPLIED)) break;
if(!weapon||strcmp(weapon->name,"quarterstaff")) {
if(op->type==PLAYER) {
new_draw_info(NDI_UNIQUE, 0,op,"The spell fails to transform your weapon.");
op->contr->count_left=0;
}
return 0;
}
tmp=arch_to_object(at);
if(op->type==PLAYER && !QUERY_FLAG(weapon,FLAG_CURSED) &&
!QUERY_FLAG(weapon,FLAG_DAMNED)) {
CLEAR_FLAG(tmp, FLAG_MONSTER);
tmp->stats.exp=0;
add_friendly_object(tmp);
tmp->type=GOLEM;
set_owner(tmp,op);
op->contr->golem=tmp;
/* give the player control of the golem */
op->contr->shoottype=range_scroll;
} else {
if(QUERY_FLAG(op, FLAG_FRIENDLY)&&(!QUERY_FLAG(weapon,FLAG_CURSED)||
!QUERY_FLAG(weapon,FLAG_DAMNED)) ) {
object *owner = get_owner(op);
if(owner != NULL) /* For now, we transfer ownership */
set_owner(tmp,owner);
tmp->move_type = PETMOVE;
add_friendly_object(tmp);
SET_FLAG(tmp, FLAG_FRIENDLY);
}
SET_FLAG(tmp, FLAG_MONSTER);
}
/* ok, tailor the serpent's characteristics based on the weapon */
tmp->stats.wc = op->stats.wc - SP_level_strength_adjust(op,spellnum);
tmp->stats.hp = SP_PARAMETERS[spellnum].bdur +
4 * SP_level_strength_adjust(op,spellnum);
tmp->stats.dam = weapon->stats.dam + (2*SP_level_dam_adjust(op,spellnum));
if(tmp->stats.dam<0) tmp->stats.dam=127; /*seen this go negative!*/
tmp->attacktype=AT_POISON|AT_PHYSICAL;
if(weapon->attacktype) tmp->attacktype=tmp->attacktype|weapon->attacktype;
/* magic weapons make boss snakes ! */
if(weapon->magic) {
int magic = weapon->magic>0 ? weapon->magic : -1*weapon->magic;
tmp->speed += 0.05 * magic;
tmp->stats.dam *= (1 + magic);
tmp->stats.hp *= (1 + magic);
}
if(weapon->immune) tmp->immune = weapon->immune;
if(weapon->protected) tmp->protected = weapon->protected;
if(weapon->vulnerable) tmp->vulnerable = weapon->vulnerable;
if(weapon->slaying) {
if(tmp->slaying) free_string(tmp->slaying);
tmp->slaying = add_string(weapon->slaying);
}
/* now, remove object from op inv, insert into snake */
CLEAR_FLAG(weapon,FLAG_APPLIED);
remove_ob(weapon);
insert_ob_in_ob(weapon,tmp);
fix_player(op);
draw_inventory(op);
draw_stats(op);
new_draw_info(NDI_UNIQUE, 0,op,"Your staff becomes a serpent and leaps to the ground!");
/* make experience increase in proportion to the strength of
* the summoned creature. */
tmp->stats.exp *= SP_level_spellpoint_cost(op,spellnum)/spells[spellnum].sp;
tmp->speed_left= -1;
tmp->x=op->x+freearr_x[dir],tmp->y=op->y+freearr_y[dir];
tmp->direction=dir;
insert_ob_in_map(tmp,op->map);
return 1;
}
/* cast_daylight() - changes the map darkness level *lower* */
int cast_daylight ( object *op ) {
int success = 0;
mapstruct *m=op->map;
if(!m) return 0; /* shouldnt happen */
if(!(success=change_map_light(m,-1)))
new_draw_info(NDI_UNIQUE,0,op,"It can be no brighter here.");
return success;
}
/* cast_nightfall() - changes the map darkness level *higher* */
int cast_nightfall ( object *op ) {
int success=0;
mapstruct *m=op->map;
if(!m) return 0; /* shouldnt happen */
if(!(success=change_map_light(m,1)))
new_draw_info(NDI_UNIQUE,0,op,"It can be no darker here.");
return success;
}
/* cast_faery_fire() - this spell primary purpose is to light
* up all single-space monsters on a map. Magic immune and
* multi-space monsters are currently not supposed to light
* up. If USE_LIGHTING is not defined, this spell is only
* capable of doing minor fire damage. I hacked this out of
* the destruction code. -b.t.
*/
int cast_faery_fire(object *op) {
int r,dam,i,j,success=0,factor;
object *tmp;
if(op->type!=PLAYER)
return 0;
/* the smaller this is, the longer it glows */
factor=SP_PARAMETERS[SP_FAERY_FIRE].bdur
+ SP_level_strength_adjust(op,SP_FAERY_FIRE);
r=SP_PARAMETERS[SP_FAERY_FIRE].bdam
+SP_level_dam_adjust(op,SP_FAERY_FIRE);
r = 5;
factor = 10;
dam=(SK_level(op)/10)+1;
for(i= -r;i<r;i++)
for(j= -r;j<r;j++) {
if(out_of_map(op->map,op->x+i,op->y+j))
continue;
tmp=get_map_ob(op->map,op->x+i,op->y+j);
while(tmp!=NULL&&(!QUERY_FLAG(tmp, FLAG_ALIVE)
||tmp->type==PLAYER||tmp->more||tmp->head
||tmp->immune==AT_MAGIC))
tmp=tmp->above;
if(tmp==NULL)
continue;
#ifndef USE_LIGHTING
success += hit_player(tmp,dam,op,AT_FIRE|AT_MAGIC);
#else
if(make_object_glow(tmp,1,factor)) {
object *effect=get_archetype("detect_magic");
success++;
if(effect){
effect->x = tmp->x;
effect->y = tmp->y;
insert_ob_in_map(effect,op->map);
}
}
#endif
}
return success;
}
#ifdef 0
I set this out for now because the object code wont allow
non-living objects to glow viz force in their inventory. This
is because (Ibelive) the code doesnt currently allow non-living
objects (exception: containers) to have an inventory. I may be
wrong, but have not time to check it out now..
/* cast_glow() */
int cast_glow( object *op, int dir) {
int x,y,time,radius;
object *tmp;
if(!dir) return 0;
x=op->x+freearr_x[dir];
y=op->y+freearr_y[dir];
if(out_of_map(op->map,x,y)) return 0;
for(tmp=get_map_ob(op->map,x,y);tmp;tmp=tmp->above) {
if(QUERY_FLAG(tmp, FLAG_ALIVE)||tmp->immune&AT_MAGIC)
continue;
if(QUERY_FLAG(tmp,FLAG_IS_FLOOR)) break;
}
if(!tmp||QUERY_FLAG(tmp,FLAG_IS_FLOOR)) { /* argh. nothing on the floor,
* lets look at the inventory */
if(op->inv) tmp = op->inv; /* take first item */
else return 0;
}
time = (SP_PARAMETERS[SP_GLOW].bdur
- (10*SP_level_strength_adjust(op,SP_GLOW)));
radius=SP_PARAMETERS[SP_GLOW].bdam
+ SP_level_dam_adjust(op,SP_GLOW);
LOG(llevDebug,"cast_glow got item: %s to attempt glow\n");
/* ok, lets try to make tmp glow */
return make_object_glow(tmp,radius,time);
}
#endif
/* make_object_glow() - currently only makes living objects glow.
* we do this by creating a "force" and inserting it in the
* object. if time is 0, the object glows permanently. To truely
* make this work for non-living objects, we would have to
* give them the capability to have an inventory. b.t.
*/
int make_object_glow(object *op, int radius, int time) {
object *tmp;
/* some things are unaffected... */
if(op->path_denied&PATH_LIGHT)
return 0;
tmp=get_archetype("force");
tmp->speed = 0.000001 * time;
tmp->glow_radius=radius;
tmp->x=op->x,tmp->y=op->y;
if(tmp->speed<=0) tmp->speed = 0; /* safety */
tmp=insert_ob_in_ob(tmp,op);
if(!tmp->env||op!=tmp->env) {
LOG(llevError,"make_object_glow() failed to insert glowing force in %s\n",
op->name);
return 0;
}
return 1;
}